package ms.unicorn.diagram.part;

import java.lang.reflect.InvocationTargetException;
import java.util.Collections;

import ms.unicorn.Element;
import ms.unicorn.dsl.ILanguageDiagram;
import ms.unicorn.dsl.IModelLanguage;
import ms.unicorn.dsl.LanguageRegistry;
import ms.unicorn.provider.UnicornItemProviderAdapterFactory;
import ms.unicorn.util.UnicornResourceItemProviderAdapterFactory;

import org.eclipse.core.commands.ExecutionException;
import org.eclipse.core.resources.IFile;
import org.eclipse.core.resources.IResource;
import org.eclipse.core.resources.ResourcesPlugin;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IAdaptable;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.NullProgressMonitor;
import org.eclipse.core.runtime.Path;
import org.eclipse.emf.common.command.AbstractCommand;
import org.eclipse.emf.common.command.Command;
import org.eclipse.emf.common.ui.URIEditorInput;
import org.eclipse.emf.common.util.URI;
import org.eclipse.emf.ecore.EObject;
import org.eclipse.emf.ecore.provider.EcoreItemProviderAdapterFactory;
import org.eclipse.emf.ecore.resource.Resource;
import org.eclipse.emf.edit.provider.ComposedAdapterFactory;
import org.eclipse.emf.edit.provider.ReflectiveItemProviderAdapterFactory;
import org.eclipse.emf.edit.provider.resource.ResourceItemProviderAdapterFactory;
import org.eclipse.emf.edit.provider.resource.ResourceSetItemProvider;
import org.eclipse.emf.edit.ui.dnd.LocalTransfer;
import org.eclipse.emf.edit.ui.dnd.ViewerDragAdapter;
import org.eclipse.emf.edit.ui.provider.AdapterFactoryContentProvider;
import org.eclipse.emf.edit.ui.provider.AdapterFactoryLabelProvider;
import org.eclipse.emf.edit.ui.provider.UnwrappingSelectionProvider;
import org.eclipse.gmf.runtime.common.core.command.CommandResult;
import org.eclipse.gmf.runtime.diagram.ui.resources.editor.document.IDocument;
import org.eclipse.gmf.runtime.diagram.ui.resources.editor.document.IDocumentProvider;
import org.eclipse.gmf.runtime.emf.commands.core.command.AbstractTransactionalCommand;
import org.eclipse.gmf.runtime.notation.Diagram;
import org.eclipse.jface.action.Action;
import org.eclipse.jface.action.IMenuListener;
import org.eclipse.jface.action.IMenuManager;
import org.eclipse.jface.action.MenuManager;
import org.eclipse.jface.action.Separator;
import org.eclipse.jface.dialogs.ErrorDialog;
import org.eclipse.jface.dialogs.MessageDialog;
import org.eclipse.jface.operation.IRunnableWithProgress;
import org.eclipse.jface.viewers.DoubleClickEvent;
import org.eclipse.jface.viewers.IDoubleClickListener;
import org.eclipse.jface.viewers.ISelection;
import org.eclipse.jface.viewers.ISelectionProvider;
import org.eclipse.jface.viewers.StructuredSelection;
import org.eclipse.jface.viewers.StructuredViewer;
import org.eclipse.jface.viewers.TreeViewer;
import org.eclipse.swt.SWT;
import org.eclipse.swt.dnd.DND;
import org.eclipse.swt.dnd.Transfer;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Menu;
import org.eclipse.ui.IEditorInput;
import org.eclipse.ui.ISelectionService;
import org.eclipse.ui.IWorkbenchPage;
import org.eclipse.ui.PartInitException;
import org.eclipse.ui.PlatformUI;
import org.eclipse.ui.actions.WorkspaceModifyOperation;
import org.eclipse.ui.part.FileEditorInput;
import org.eclipse.ui.views.contentoutline.ContentOutlinePage;

//See also: DiagramEditor$DiagramOutlinePage
public class UnicornOutlinePage extends ContentOutlinePage implements IMenuListener, ISelectionProvider {

	private IDocumentProvider documentProvider;
	private UnicornDiagramEditor diagramEditor;
	private IEditorInput editorInput;
	private Diagram diagram;
	private Action actionSetDefaultDiagram;
	private TreeViewer viewer;
	private Object selectedElement;
	private OpenDiagramAction actionOpenDiagram;
	private OpenDiagramAction actionOpenDiagramNewWindow;
	private OpenDiagramAction defaultAction;
	
	class CreateDiagramAction extends Action {
		
		private ILanguageDiagram diagram;

		public CreateDiagramAction(ILanguageDiagram diagram) {
			super(String.format("Create new %s", diagram.getText()), diagram.getSmallImage());
			this.diagram = diagram;
		}
		
		@Override
		public void run() {
			IRunnableWithProgress op = new WorkspaceModifyOperation(null) {
				protected void execute(IProgressMonitor monitor)
						throws CoreException, InterruptedException {
					Resource newDiagram = UnicornDiagramEditorUtil.createDiagram(
							diagram.getLanguage(),
							diagram,
							diagramEditor.getEditingDomain().getResourceSet().getResources().get(0).getURI(),
							null, monitor);
					if (diagram != null) {
						try {
							UnicornDiagramEditorUtil.openDiagram(newDiagram);
						} catch (PartInitException e) {
							ErrorDialog.openError(UnicornOutlinePage.this.getSite().getShell(),
									Messages.UnicornCreationWizardOpenEditorError,
									null, e.getStatus());
						}
					}
				}
			};
			try {
				
				getSite().getWorkbenchWindow().run(false, true, op);
			} catch (InterruptedException e) {
				
			} catch (InvocationTargetException e) {
				if (e.getTargetException() instanceof CoreException) {
					ErrorDialog.openError(getSite().getShell(),
							Messages.UnicornCreationWizardCreationError, null,
							((CoreException) e.getTargetException()).getStatus());
				} else {
					UnicornDiagramEditorPlugin.getInstance().logError(
							"Error creating diagram", e.getTargetException()); //$NON-NLS-1$
				}
			}
		}
	}

	class OpenDiagramAction extends Action {
		
		private boolean openInNewEditor;

		public OpenDiagramAction(String text, boolean openInNewEditor) {
			super(text);
			this.openInNewEditor = openInNewEditor;
		}

		@Override
		public void run() {
			Diagram selectedDiagram = getSelectedDiagram();
			if (selectedDiagram == null && getSelectedDomainDiagram() != null) {
				ms.unicorn.Diagram selectedDomainDiagram = getSelectedDomainDiagram();
				for(EObject content: selectedDomainDiagram.eResource().getContents())
					if (content instanceof Diagram) {
						if (selectedDomainDiagram.equals(((Diagram) content).getElement())) {
							selectedDiagram = (Diagram) content;
							break;
						}
					}
			}
			if (selectedDiagram == null)
				return;
			String path = selectedDiagram.eResource().getURIFragment(selectedDiagram);
			
			URI uri = selectedDiagram.eResource().getURI();
			uri = URI.createURI(uri.toString() + "#" + path);
			URIEditorInput input = new URIEditorInput(uri);
			
			if (openInNewEditor) {
				IWorkbenchPage page = PlatformUI.getWorkbench().getActiveWorkbenchWindow().getActivePage();
				try {
					page.openEditor(input, UnicornDiagramEditor.ID);
				} catch (PartInitException e) {
					e.printStackTrace();
				}
			}
			else  {
				if (diagramEditor.isDirty()) {
					MessageDialog dialog = new MessageDialog(diagramEditor.getSite().getShell(), "Save Changes", null,
							String.format("Do you want to save changes in %s", diagramEditor.getTitle()),
							MessageDialog.QUESTION_WITH_CANCEL,  new String[] {"Yes", "No", "Cancel"}, SWT.SHEET);
					switch(dialog.open()) {
					case 0: diagramEditor.doSave(new NullProgressMonitor());
						break;
					case 2:
						return; 
					}
				}
				diagramEditor.setInput(input);
			}

		}
	}
	
	public UnicornOutlinePage(IDocumentProvider documentProvider,
			UnicornDiagramEditor unicornDiagramEditor) {
		this.documentProvider = documentProvider;
		this.diagramEditor = unicornDiagramEditor;
	}

	public void setInput(IEditorInput editorInput) {
		this.editorInput = editorInput;
		if (editorInput != null) {
			this.diagram = (Diagram) documentProvider.getDocument(editorInput).getContent();
		}
		else
			this.diagram = null;
	}
	
	@Override
	public void dispose() {
		getSite().setSelectionProvider(null);
		super.dispose();
	}
	
	@Override
	public void createControl(Composite parent) {
		super.createControl(parent);
		
		this.viewer= getTreeViewer();
		viewer.addSelectionChangedListener(this);
		
		//ComposedAdapterFactory adapterFactory = new ComposedAdapterFactory(ComposedAdapterFactory.Descriptor.Registry.INSTANCE);
		
		ComposedAdapterFactory adapterFactory = new ComposedAdapterFactory(Collections.EMPTY_LIST);
		
		adapterFactory.addAdapterFactory(new UnicornResourceItemProviderAdapterFactory());
		adapterFactory.addAdapterFactory(new UnicornItemProviderAdapterFactory());
		adapterFactory.addAdapterFactory(new EcoreItemProviderAdapterFactory());
		//adapterFactory.addAdapterFactory(new ReflectiveItemProviderAdapterFactory());
		
		
		viewer.setContentProvider(new AdapterFactoryContentProvider(adapterFactory));
		viewer.setLabelProvider(new AdapterFactoryLabelProvider(adapterFactory));
		
		viewer.setInput(diagramEditor.getEditingDomain().getResourceSet());
		
		viewer.addDoubleClickListener(new IDoubleClickListener() {
			
			@Override
			public void doubleClick(DoubleClickEvent event) {
				if (defaultAction != null)
					defaultAction.run();
			}
		});
		
		createContextMenuFor(viewer);
		//ISelectionService service = (ISelectionService) getSite().getService(ISelectionService.class);
		getSite().setSelectionProvider(this);

	}
	
	protected void createContextMenuFor(StructuredViewer viewer) {
		MenuManager contextMenu = new MenuManager("#PopUp");
		contextMenu.add(new Separator("additions"));
		contextMenu.setRemoveAllWhenShown(false);
		contextMenu.addMenuListener(this);
		Menu menu= contextMenu.createContextMenu(viewer.getControl());
		viewer.getControl().setMenu(menu);
		getSite().registerContextMenu("#menu-outline", contextMenu, new UnwrappingSelectionProvider(viewer));

		int dndOperations = DND.DROP_COPY | DND.DROP_MOVE | DND.DROP_LINK;
		Transfer[] transfers = new Transfer[] { LocalTransfer.getInstance() };
		viewer.addDragSupport(dndOperations, transfers, new ViewerDragAdapter(viewer));
		//viewer.addDropSupport(dndOperations, transfers, new EditingDomainViewerDropAdapter(editingDomain, viewer));
		
		this.actionSetDefaultDiagram = new Action("Set diagram as default") {
			public void run() {
				final ms.unicorn.Diagram selectedDomainDiagram = getSelectedDomainDiagram();
				if (selectedDomainDiagram != null) {
					Resource resource = selectedDomainDiagram.eResource();
					for(EObject content: resource.getContents())
						if (content instanceof ms.unicorn.Diagram) {
							final ms.unicorn.Diagram diagram = (ms.unicorn.Diagram) content;
							Command cmd = new AbstractCommand() {

								@Override
								public void execute() {
									//diagram.setProperty("DefaultDiagram", Boolean.toString(diagram.equals(selectedDiagram)));
									IModelLanguage.DEFAULT_DIAGRAM_ATTR.setValue(diagram, diagram.equals(selectedDomainDiagram));
								}

								@Override
								public void redo() {
									
								}
								
								@Override
								public boolean canExecute() {
									return true;
								}
								
							};
							diagramEditor.getEditingDomain().getCommandStack().execute(cmd);
						}
				}
			};
		};
		
		this.actionOpenDiagram = new OpenDiagramAction("Open diagram", false);
		this.actionOpenDiagramNewWindow = new OpenDiagramAction("Open diagram in new editor", true);
		
		contextMenu.add(actionOpenDiagram);
		contextMenu.add(actionOpenDiagramNewWindow);
		contextMenu.add(actionSetDefaultDiagram);
		contextMenu.add(new Separator());
		
		if (diagram != null) {
			Element e = (Element) diagram.getElement();
			IModelLanguage language = LanguageRegistry.INSTANCE.getLanguage(e.getLanguage());
			for(ILanguageDiagram diagram: language.getDiagrams())
				contextMenu.add(new CreateDiagramAction(diagram));
		}
	}

	@Override
	public void menuAboutToShow(IMenuManager manager) {
		ISelection selection = viewer.getSelection();
		this.defaultAction = null;
		this.selectedElement = null;
		if (selection instanceof StructuredSelection) {
			selectedElement = ((StructuredSelection) selection).getFirstElement();
			if (getSelectedDomainDiagram() != null)
				this.defaultAction = actionOpenDiagram;
		}
		actionSetDefaultDiagram.setEnabled(getSelectedDomainDiagram() != null);
		actionOpenDiagram.setEnabled(getSelectedDiagram() != null || getSelectedDomainDiagram() != null);
		actionOpenDiagramNewWindow.setEnabled(actionOpenDiagram.isEnabled());
	}

	public ms.unicorn.Diagram getSelectedDomainDiagram() {
		return (ms.unicorn.Diagram) (selectedElement instanceof ms.unicorn.Diagram ? selectedElement : null);
	}
	
	public Diagram getSelectedDiagram() {
		return (Diagram) (selectedElement instanceof Diagram ? selectedElement : null);
	}
}
